package gek.uni.diplom.ocl.example.generators.visitors.hierarhical;

import gek.uni.diplom.ocl.example.exceptions.ErrorException;
import gek.uni.diplom.ocl.example.managers.ErrorManager;
import gek.uni.diplom.ocl.example.patterns.Pattern;
import gek.uni.diplom.ocl.example.patterns.hierarhical.ACodeNode;
import gek.uni.diplom.ocl.example.patterns.hierarhical.Visitor;
import gek.uni.diplom.ocl.example.utils.OclEcoreUtils;

import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.SendSignalAction;
import org.eclipse.ocl.expressions.BooleanLiteralExp;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.expressions.OperationCallExp;
import org.eclipse.ocl.expressions.PropertyCallExp;
import org.eclipse.ocl.expressions.VariableExp;
import org.eclipse.ocl.utilities.AbstractVisitor;
import org.eclipse.ocl.utilities.PredefinedType;

public class SourceCodeGeneratorVisitor implements Visitor<ACodeNode>{

  /**
   * Шаблон, в котором собран результат подготовки выражения для генерации
   * исходного кода
   */
  private VariableExpCodeNode pattern = null;

  private Set<String> declaredVariableNames = new TreeSet<String>();

  public SourceCodeGeneratorVisitor(final VariableExpCodeNode commonPattern) {
    this.pattern = commonPattern;
  }

  @Override
  public String visitVariableExp(VariableExp<EClassifier, EParameter> v) {
    StringBuilder result = new StringBuilder();
    // String varName = v.getName();
    // if (false == declaredVariableNames.contains(varName)) {
    // result.append(pattern.getDeclarationString(varName)).append("\n");
    // result.append(pattern.getInitializationSection(varName)).append("\n");
    // declaredVariableNames.add(varName);
    // }
    return result.toString();
  }

  @Override
  protected String handlePropertyCallExp(
      PropertyCallExp<EClassifier, EStructuralFeature> callExp,
      String sourceResult, List<String> qualifierResults) {
    return sourceResult;
  }

  @SuppressWarnings("unchecked")
  @Override
  protected String handleOperationCallExp(

  OperationCallExp<EClassifier, EOperation> callExp, String sourceResult,
      List<String> argumentResults) {
    // Если sourceResult == null, то скорее всего мы разбираем выражение в
    // правой части.
//    if (sourceResult == null) return null;
    StringBuilder result =
        sourceResult != null ? new StringBuilder(sourceResult)
            : new StringBuilder();

    if (!OclEcoreUtils.checkIsLeafExp(callExp)) return sourceResult;

    int opCode = callExp.getOperationCode();

    switch (opCode) {
    case PredefinedType.EQUAL:
      OCLExpression<EClassifier> source = callExp.getSource();
      OCLExpression<EClassifier> arg = callExp.getArgument().get(0);
      if (false == arg instanceof BooleanLiteralExp) {
        try {
          throw new ErrorException(
              "Ошибка генерации исходного кода для выражения "
                  + callExp.toString());
        } catch (ErrorException e) {
          ErrorManager.addError(e.getMessage());
          e.printStackTrace();
          return null;
        }
      }
      Boolean expectedResult =
          ((BooleanLiteralExp<EClassifier>) arg).getBooleanSymbol();
      String sourceString = null;
      if (expectedResult) {
        sourceString = pattern.getTrueInitialization(source.toString());
      } else
        sourceString = pattern.getFalseInitialization(source.toString());
      result.append(pattern.getSetterInitString(source.toString()))
          .append("\n");
      // result.append(pattern.getOperationInitString(source.toString()));
      result.append(sourceString);
      return result.toString();

    default:
      break;
    }
    return result.toString();
  }

}
